#include <plib.h>

/* error handling */
void U_error(int b,char *msg,...){
  if(b){
    char out1[256],out2[256];
    va_list list;

    va_start(list, msg);
    vsprintf(out1, msg, list);
    va_end(list);
    sprintf(out2, "FATAL ERROR >> %s\n", out1);

    fprintf(stderr,out2);
    fflush(stderr);

    exit(1);
  }
}
int U_Warning(int b,char *msg,...){
  if(b){
    char out1[256],out2[256];
    va_list list;

    va_start(list, msg);
    vsprintf(out1, msg, list);
    va_end(list);  
    sprintf(out2, "WARNING >> %s\n", out1);

    fprintf(stderr,out2);
    fflush(stderr);

    return 1;
  }
  return 0;
}

/* memory allocation */
void** U_smalloc(int n, size_t b,char *msg){
  void *ptr;
  ERROR(((ptr = malloc(n*b)) == NULL,msg));
#ifdef DEBUG
  dbg_alloc(ptr,msg);
#endif
  return (void **)ptr;
}
void** U_ssmallocdouble(int nb,double ini,char *msg){
  double *ptr;
  int i;
  ptr = U_malloc(nb,double,msg);
  for(i=0;i<nb;i++)
    ptr[i] = ini;

  return (void **)ptr;
}
void** U_ssmallocint(int nb,int ini,char *msg){
  int *ptr;
  int i;
  ptr = U_malloc(nb,int,msg);
  for(i=0;i<nb;i++)
    ptr[i] = ini;

  return (void **)ptr;
}
void** U_srealloc(void *ptr,int n,size_t b,char *msg){
  void* p;
  ERROR(((p = realloc(ptr,n*b)) == NULL,msg));
#ifdef DEBUG
  dbg_realloc(ptr,p);
#endif
  return (void **)p;
}

void** U_smallocxy(int x,int y,size_t d,char *msg){ 
  int i;
  char** tmp;

  if(y>0){
    tmp = (char **)U_smalloc(1,x*sizeof(void *) + x*y*d,msg);
    tmp[0] = (char *)(tmp + x);

    /*tmp = U_malloc(x,char *, msg);
      tmp[0] = (char *)U_smalloc(x*y,d, msg);*/

    for(i=1;i<x;i++)
      tmp[i] = tmp[i-1] + (y * d);
    return (void **)tmp;
  }
  
  return NULL;
}

void** U_sreallocxy(void *p,int old_x,int old_y,int new_x,int new_y,size_t size, char *msg){
  int i;
  char **old = (char **)p;
  char **new;

  new = (char **)U_smallocxy(new_x,new_y,size,msg);
    
  for(i=0;i<old_x;i++){
    memcpy(new[i],old[i],old_y*size);
  }

  U_free(msg,&old,LEND);

  return (void **)new;
}

void U_free(char* msg,...)
{
  va_list plist;
  void **ptr;
  va_start(plist, msg);
  while((ptr = va_arg(plist, void **)) != LEND){
    if (*ptr != NULL){
#ifdef DEBUG
      dbg_free(*ptr,msg);
#endif
      free(*ptr);
    }
    *ptr = NULL;
  }
  va_end(plist);
}           
/* operations on filename */
void U_filename(char *dest, char* url){
  char *token;
  char *result;
  char tmp[256];
  strcpy(tmp,url);
  /* filenames get rid of ../ or ./ or .... or ........... */
#ifndef WINDOWS
  token = strtok(tmp,"/");
#else
  token = strtok(tmp,"\\");
#endif
  if(token != NULL){
    result = token;
#ifndef WINDOWS
    while((token = strtok(NULL,"/")) != NULL)
#else
    while((token = strtok(NULL,"\\")) != NULL)
#endif
      result = token;
    strcpy(dest,result);
  }else{
    strcpy(dest,url);
  }
}

/* log file reading */

char **U_line = NULL;
int U_col = U_COL;
int U_size = U_SIZE;

static int U_putchar(int, int, int);

int U_readLine(FILE *fp){
  int ctrl=1, read = -1, c = 0;
  int i, ch;

  /* initial allocation */
  if(U_line == NULL)
    U_line = U_mallocxy(U_col,U_size,char,"U_readLine error code 1\n");

  for(i=0;i<U_col;i++){
    strcpy(U_line[i],"");
  }
  while((ch = getc(fp)) != EOF && ch != '\n'){
    if(ctrl == 0 || (read == -1 && (ch == '#' || ch == '-'))){
      ctrl = 0;
      continue;
    }
    if(read == -1 && ch == (int)'|'){
      read++;
      continue;
    }
    if(read >= 0 && ch == (int)'|'){
      ch = '\0';
      WARNING((U_putchar(c,read,(int)ch) == 0,"U_readLine error code 3 : error in U_putchar\n"),0);
      read = 0;
      c++;
      continue;
    }
    if(read >= 0){
      if(!isspace(ch)){
	WARNING((U_putchar(c,read,ch) == 0,"U_readLine error code 3 : error in U_putchar\n"),0);
	read++;
      }
    }
  }
  if(ctrl == 0)
    return 0;
  else
    return c;
}

void U_freeline(void){
  U_free("U_freeline error code 1\n",&U_line,LEND);
}

int U_putchar(int c, int p, int ch){
  if(c >= U_col){
    U_line = U_reallocxy(U_line,U_col,U_size,U_col * 2,U_size,char,"U_putchar error code 1\n");
    U_col *= 2;
  }

  if(p >= U_size){
    U_line = U_reallocxy(U_line,U_col,U_size,U_col,U_size * 2,char,"U_putchar error code 2\n");
    U_size *= 2;
  }

  U_line[c][p] = (char)ch;
  return 1;
}

/* serialize utils */
int U_serializeIntArray(int *array,int size, FILE* fp){
  int write = 0;
  WARNING(((write = fwrite(array,sizeof(int),size,fp)) != size,"U_serializeIntArray error code 1 : written %d elements expected %d\n",write,size),0);
  return 1;
}

int* U_unserializeIntArray(int size, FILE* fp,...){
  int *array = NULL,read = 0;
  int *tmp;
  va_list plist;
  char buf[256];

  va_start(plist, fp);
  while((tmp = va_arg(plist, int *)) != (int *)LEND){
    array = tmp;
  }
  va_end(plist);
  sprintf(buf,"%s (%d)\n","U_unserializeIntArray error code 1",size);
  if(array == NULL || array == (int *)LEND)
    array = U_malloc(size,int,buf);

  WARNING(((read = fread(array,sizeof(int),size,fp)) != size,"unserializeintarray error code 2 : readden %d elements expected %d\n",read,size),NULL);

  return array;
}

int U_serializeStr(char *s,FILE* fp){
  int write = strlen(s) + 1;
  WARNING((fwrite(&write,sizeof(int),1,fp) != 1,"U_serializeStr error code 1 : the string wasn't correctly written\n"),0);

  WARNING((fwrite(s,sizeof(char),write,fp) != write,"U_serializeString error code 2 : the string wasn't correctly written\n"),0);
  return 1;
}
char* U_unserializeStr(FILE* fp){
  int read,i;
  char *s;

  WARNING((fread(&read,sizeof(int),1,fp) != 1,"U_unserializeStr error code 1 : cannot read string size\n"),NULL);

  s = U_malloc(read,char,"U_unserializeStr error code 2\n");

  WARNING((fread(s,sizeof(char),read,fp) != read,"U_unserializeStr error code 3 : the string wasn't correctly readen (%s)\n",s),NULL);
  
  for(i=0;i<strlen(s);i++)
    if(s[i] == '\\')
      s[i] = '/';

  return s;
}

/* stat utils */
int U_intStat(int* s, int size,
	      double* min, int* min_p, 
	      double* max, int* max_p, 
	      double* avg, double* stddev, double* sum){
  int 
    i,
    min_pos = -1,
    max_pos = -1;
  double 
    s_max=0,
    s_min=-1,
    s_sum=0,
    s_sumtmp=0,
    s_avg=0.0,
    s_stddev=0.0;

  WARNING((s == NULL,"U_intStat error code 1 : NULL array passed\n"),0);

  for(i=0;i<size;i++){
    if(s[i]>s_max){
      s_max   = s[i];
      max_pos = i;
    }
    if(s[i]<s_min || s_min == -1){
      s_min   = s[i];
      min_pos = i;
    }
    s_avg = (double)(i * s_avg + s[i])/(double)(i+1);
    s_sum+= s[i];
  }
  for(i=0;i<size;i++){
    s_sumtmp += SQUARE(s[i] - s_avg);
  }
  s_stddev = sqrt((double)s_sumtmp / (double)size);

  if(min)
    *min    = s_min;
  if(min_p)
    *min_p  = min_pos;
  if(max)
    *max    = s_max;
  if(max_p)
    *max_p  = max_pos;
  if(avg)
    *avg    = s_avg;
  if(stddev)
    *stddev = s_stddev;
  if(sum)
    *sum    = s_sum;
  
  return 1;
}

int U_dbStat(double* s, int size,
	     double* min, int* min_p,
	     double* max, int* max_p,
	     double* avg, double* stddev, double* sum){
  int 
    i,
    min_pos = -1,
    max_pos = -1;
  double 
    s_max=0,
    s_min=-1,
    s_sum=0,
    s_sumtmp=0,
    s_avg=0.0,
    s_stddev=0.0;

  WARNING((s == NULL,"U_dbStat error code 1 : NULL array passed\n"),0);

  for(i=0;i<size;i++){
    if(s[i]>s_max){
      s_max   = s[i];
      max_pos = i;
    }
    if(s[i]<s_min || s_min == -1){
      s_min   = s[i];
      min_pos = i;
    }
    s_avg = (double)(i * s_avg + s[i])/(double)(i+1);
    s_sum+= s[i];
  }
  for(i=0;i<size;i++){
    s_sumtmp += SQUARE(s[i] - s_avg);
  }
  s_stddev = sqrt((double)s_sumtmp / (double)size);

  if(min)
    *min    = s_min;
  if(min_p)
    *min_p  = min_pos;
  if(max)
    *max    = s_max;
  if(max_p)
    *max_p  = max_pos;
  if(avg)
    *avg    = s_avg;
  if(stddev)
    *stddev = s_stddev;
  if(sum)
    *sum    = s_sum;
  
  return 1;
}

/* quicksort */
#define compGT(a,b) (a > b)
static void U_insertSort(T*,tblIndex,tblIndex,T**,int);
static tblIndex U_partition(T*,tblIndex,tblIndex,T**,int);

void U_insertSort(T *a, tblIndex lb, tblIndex ub,T** l,int nb) {
    T t;
    tblIndex i, j, k;
    T* tl;
    
    if(l)
      tl = U_malloc(nb,T,"U_insertSort error code 1\n");

   /**************************
    *  sort array a[lb..ub]  *
    **************************/
    for (i = lb + 1; i <= ub; i++) {
        t = a[i];
	if(l)
	  for(k = 0; k < nb; k++)
	    tl[k] = l[k][i];

        /* Shift elements down until */
        /* insertion point found.    */
        for (j = i-1; j >= lb && compGT(a[j], t); j--){
	  a[j+1] = a[j];
	  if(l)
	    for(k = 0; k < nb; k++)
	      l[k][j+1] = l[k][j];
	}

        /* insert */
        a[j+1] = t;
	if(l)
	  for(k = 0; k < nb; k++)
	    l[k][j+1] = tl[k];
    }
    if(l)
      U_free("U_insertSort error code 2\n",&tl,LEND);
}

tblIndex U_partition(T *a, tblIndex lb, tblIndex ub,T **l,int nb) {
    T t, pivot;
    tblIndex i, j, k, p;

   /*******************************
    *  partition array a[lb..ub]  *
    *******************************/

    /* select pivot and exchange with 1st element */
    p = lb + ((ub - lb)>>1);
    pivot = a[p];
    a[p] = a[lb];
    if(l)
      for(k = 0; k < nb; k++)
	l[k][p] = l[k][lb];

    /* sort lb+1..ub based on pivot */
    i = lb+1;
    j = ub;
    while (1) {
        while (i < j && compGT(pivot, a[i])) i++;
        while (j >= i && compGT(a[j], pivot)) j--;
        if (i >= j) break;
        t = a[i];
        a[i] = a[j];
        a[j] = t;
	if(l)
	  for(k = 0; k < nb; k++){
	    t = l[k][i];
	    l[k][i] = l[k][j];
	    l[k][j] = t;
	  }
        j--; i++;
    }

    /* pivot belongs in a[j] */
    a[lb] = a[j];
    a[j] = pivot;
    if(l)
      for(k = 0; k < nb; k++){
	l[k][lb] = l[k][j];
	l[k][j] = l[k][p];
      }

    return j;
}

void U_quickSort(T *a, tblIndex lb, tblIndex ub,T** l,int nb) {
    tblIndex m;

   /**************************
    *  sort array a[lb..ub]  *
    **************************/

    while (lb < ub) {

        /* quickly sort short lists */
        if (ub - lb <= 12) {
            U_insertSort(a, lb, ub, l, nb);
            return;
        }

        /* partition into two segments */
        m = U_partition (a, lb, ub, l, nb);

        /* sort the smallest partition    */
        /* to minimize stack requirements */
        if (m - lb <= ub - m) {
            U_quickSort(a, lb, m - 1, l, nb);
            lb = m + 1;
        } else {
            U_quickSort(a, m + 1, ub, l, nb);
            ub = m - 1;
        }
    }
}

#ifdef DEBUG
#define STR_SIZE 256
typedef struct memdbg{
  void *ptr;
  char alloc[STR_SIZE];
  char dealloc[STR_SIZE];
}memdbg;
#define MEMSIZE 1000000
memdbg memarr[MEMSIZE];
long memidx = 0;

void dbg_tag(int u_nb){
  static int nb = 0;
  if(u_nb != 0){
    nb = u_nb+1;
    printf(" --- %3d --- \n",u_nb);fflush(stdout);
  }else{
    printf(" --- %3d --- \n",nb++);fflush(stdout);
  }
}

void dbg_alloc(void* p, char* msg){
  ERROR((memidx >= MEMSIZE,"dbg_alloc error code 1 : MEMSIZE too small\n"));
  memarr[memidx].ptr = p;
  strncpy(memarr[memidx].alloc,msg,strlen(msg)-1);
  memarr[memidx].alloc[strlen(msg)-1] = '\0';
  strcpy(memarr[memidx].dealloc,"");
  memidx++;
}
void dbg_realloc(void* p,void* new_p){
  long i;
  for(i=0;i<memidx;i++)
    if(memarr[i].ptr == p)
      memarr[i].ptr = new_p;
}
void dbg_free(void* p, char* msg){
  long i,notice = 0 ,find = 0;
  for(i=0;i<memidx;i++){
    if(memarr[i].ptr == p){
      if(strlen(memarr[i].dealloc) != 0)
	notice++;
      else{
	strncpy(memarr[i].dealloc,msg,strlen(msg)-1);
	memarr[memidx].dealloc[strlen(msg)-1] = '\0';
      }
      find++;
    }
  }
  if(find != 0){
    if(notice < find)
      return;
    for(i=0;i<memidx;i++){
      if(memarr[i].ptr == p){
	NOTICE((1,"dbg_free : pointer %p allocated by %s\n",p,memarr[i].alloc));
      }
    }
    ERROR((1,"dbg_free : pointer %p was freed twice second time by %s\n",p,msg));
  }

  dbg_printmem();
  ERROR((1,"dbg_free : pointer %p was not allocated and is freed by %s\n",p,msg));
}
void dbg_isemptymem(){
  int error = 0,i;
  for(i=0;i<memidx;i++){
    if(strcmp(memarr[i].dealloc,"")==0){
      fprintf(stderr,"%10p %50s %50s\n",memarr[i].ptr,memarr[i].alloc,memarr[i].dealloc);
      error++;
    }
  }
  if(error != 0)
    ERROR((1,"%d unfreed elements\n",error));
}
void dbg_printmem(){
  long i;
  fprintf(stderr,"%9s | %48s | %48s\n","pointer","allocated","freed");
  fprintf(stderr,"---------------------------------------------------------------------------------------------------------------\n");
  for(i=0;i<memidx;i++){
    fprintf(stderr,"%10p %50s %50s\n",memarr[i].ptr,memarr[i].alloc,memarr[i].dealloc);
  }
  fprintf(stderr,"---------------------------------------------------------------------------------------------------------------\n");
  fflush(stderr);
}

void dbg_freemem(){
  long i,j;

  for(j=0;j<memidx;j++){
    memarr[j].ptr = NULL;
    for(i=0;i<STR_SIZE;i++){
      memarr[j].alloc[i] = '\0';
      memarr[j].dealloc[i] = '\0';
    }
  }
  memidx = 0;
}
#endif
